<HTML>
<META http-equiv="PICS-Label" content='(PICS-1.1 "http://www.rsac.org/ratingsv01.html" l gen true comment "RSACi North America Server" by "RodStephens@vb-helper.com" for "http://www.vb-helper.com" on "1998.03.17T18:18-0800" r (n 0 s 0 v 0 l 0))'>
<HEAD>
<TITLE>VB Helper: HowTo: Make a drawing application in VB .NET</TITLE>
<META NAME="Author" CONTENT="Rod Stephens">
<META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=iso-8859-1">
<META HTTP-EQUIV="Keywords" CONTENT="draw, drawing, Drawable, serialization, XmlSerializer">
<META NAME="Keywords" CONTENT="draw, drawing, Drawable, serialization, XmlSerializer">
<META NAME="Description" CONTENT="VB Helper: HowTo: Make a drawing application in VB .NET">
<META NAME="Copyright" CONTENT="Copyright 1997-2008, Rocky Mountain Computer Consulting, Inc.">
<META NAME="Rating" CONTENT="General">
<META NAME="Robots" CONTENT="All">
<LINK REL="icon" HREF="http://www.vb-helper.com/vbhelper_icon.ico">
</HEAD>

<BODY BGCOLOR="#E1F3FF" BACKGROUND="bg_blue.jpg">

  <!-- This table contains the banner, menu column, and content. -->
  <TABLE WIDTH="100%" BORDER="0" CELLSPACING="0" CELLPADDING="0">
    <!-- ****** -->
    <!-- Banner -->
    <!-- ****** -->
    <TR><TD>
      <TABLE WIDTH="100%" BORDER="0" CELLSPACING="0" CELLPADDING="0" ALIGN="Left">
        <TR>
          <TD BACKGROUND="banner_mid.jpg"><IMG SRC="banner_l.jpg" WIDTH="410" HEIGHT="64"></TD>
          <TD BGCOLOR="#E1F3FF"><IMG SRC="banner_r.jpg" WIDTH="32" HEIGHT="64"></TD>
        </TR>
      </TABLE>
    </TD></TR>
    <TR><TD>&nbsp;</TD></TR>

    <!-- Menu column and content -->
    <TR><TD>
      <TABLE WIDTH="100%" BORDER="0" CELLSPACING="2" CELLPADDING="0">
        <!-- *********** -->
        <!-- Menu column -->
        <!-- *********** -->
        <TR>
          <TD ALIGN="Left" VALIGN="Top">
            <TABLE BORDER="0" CELLSPACING="0" CELLPADDING="0">
              <!-- Menu top -->
              <TR>
                <TD><IMG SRC="blue_ul.jpg" WIDTH="16" HEIGHT="16"></TD>
                <TD><IMG SRC="blue_um.jpg" WIDTH="93" HEIGHT="16"></TD>
                <TD><IMG SRC="blue_ur.jpg" WIDTH="16" HEIGHT="16"></TD>
              </TR>

              <!-- Menu entries -->
              <TR BGCOLOR="#7FCFFF">
                <TD BACKGROUND="blue_l.jpg" WIDTH="16">&nbsp;</TD>
                <TD><CENTER>
                  <A HREF="index.html"><B><FONT SIZE="+1">Home</FONT></B></A><BR>
                  <A HREF="search.html"><B>Search</B></A><BR>
                  &nbsp;<BR>
                  <A HREF="whats_new.html"><B>What's New</B></A><BR>
                  <A HREF="index_categories.html"><B>Index</B></A><BR>
                  <A HREF="books.html"><B>Books</B></A><BR>
                  <A HREF="links.html"><B>Links</B></A><BR>
                  <A HREF="http://www.topica.com/lists/VBHelperQA/read"><B>Q &amp; A</B></A><BR>
                  <A HREF="newsletter.html"><B>Newsletter</B></A><BR>
                  <A HREF="banners.html"><B>Banners</B></A><BR>
                  &nbsp;<BR>
                  <A HREF="http://astore.amazon.com/vbhelper/about"><B>Bookstore...</B></A><BR>
                  &nbsp;<BR>
                  <A HREF="mailto:feedback@vb-helper.com"><B>Feedback</B></A><BR>
                  <A HREF="tip_jar.html"><B>Tip Jar</B></A><BR>
                  &nbsp;<BR>
                  <A HREF="feed.xml"><IMG SRC="xml_rss.jpg" BORDER="0" ALT="XML RSS Feed"></A><BR>
                </CENTER></TD>
                <TD BACKGROUND="blue_r.jpg" WIDTH="16">&nbsp;</TD>
              </TR>

              <!-- Menu bottom -->
              <TR>
                <TD><IMG SRC="blue_ll.jpg" WIDTH="16" HEIGHT="16"></TD>
                <TD><IMG SRC="blue_lm.jpg" WIDTH="93" HEIGHT="16"></TD>
                <TD><IMG SRC="blue_lr.jpg" WIDTH="16" HEIGHT="16"></TD>
              </TR>

              <TR><TD HEIGHT="50" COLSPAN="3">&nbsp;</TR></TD>

              <!-- ******** -->
              <!-- Partners -->
              <!-- ******** -->

              <!-- MVP -->
              <TR><TD COLSPAN="3"><A HREF="http://www.mvps.org"><IMG SRC="mvp_logo.gif" BORDER="0"></A></TR></TD>
              <TR><TD HEIGHT="20" COLSPAN="3">&nbsp;</TR></TD>

              <!-- MSDN VB Community -->
              <TR><TD COLSPAN="3" ALIGN="Center">
<A HREF="http://msdn.microsoft.com/vbasic/community/default.aspx">MSDN Visual Basic Community</A>
              </TD></TR>
              <TR><TD HEIGHT="50" COLSPAN="3">&nbsp;</TR></TD>

              <!-- Amazon Recommends -->
              <TR><TD COLSPAN="3" ALIGN="Center">
<!-- -->
                <SCRIPT SRC="http://rcm.amazon.com/e/cm?t=vbhelper&l=rc1&p=2&o=1" TYPE="text/JavaScript"></SCRIPT>
<!-- -->
                <NOSCRIPT>
                  <TABLE BORDER="0" CELLPADDING="0" CELLSPACING="0" WIDTH="125">
                    <TR><TD>
                      <MAP NAME="boxmap">
                        <AREA SHAPE="RECT" COORDS="18, 263, 105, 274" HREF="http://rcm.amazon.com/e/cm/privacy-policy.html?o=1">
                        <AREA COORDS="0,0,10000,10000" HREF="http://www.amazon.com/exec/obidos/redirect-home/vbhelper">
                      </MAP>
                      <img src="http://rcm-images.amazon.com/images/G/01/associates/amzn_recommends/gnbox125x275.gif" width="125" height="275" border="0" usemap="#boxmap">
                    </TD></TR>
                  </TABLE>
                </NOSCRIPT>
              </TD></TR>
              <TR><TD HEIGHT="50" COLSPAN="3">&nbsp;</TR></TD>

              <!-- Wiley -->
              <TR><TD COLSPAN="3" ALIGN="Center" BACKGROUND="sandpaper.gif"><A HREF="http://service.bfast.com/bfast/click?bfmid=37920629&siteid=40078832&bfpage=computer_science" TARGET="_top"><IMG SRC="wiley_logo.gif" BORDER="0" ALIGN="Center" WIDTH="77" HEIGHT="105"></TD></TR>
              <TR><TD HEIGHT="10" COLSPAN="3">&nbsp;</TR></TD>

              <!-- Que -->
              <TR><TD COLSPAN="3" ALIGN="Center"><A HREF="http://www.quepublishing.com"><IMG SRC="que.gif" BORDER="0" ALIGN="Center" WIDTH="121" HEIGHT="36"></A></TD></TR>
              <TR><TD HEIGHT="10" COLSPAN="3">&nbsp;</TR></TD>

              <!-- Amazon -->
              <TR><TD COLSPAN="3" ALIGN="Center"><A HREF="amazon.html"><IMG SRC="amazon_logo.gif" BORDER="0" ALIGN="Center" WIDTH="68" HEIGHT="65"></A></TD></TR>
              <TR><TD HEIGHT="10" COLSPAN="3">&nbsp;</TR></TD>

              <!-- ********** -->
              <!-- More stuff -->
              <!-- ********** -->

            </TABLE>
          </TD>

          <!-- A little space between the menus and content -->
          <TD WIDTH="5">&nbsp;</TD>

          <!-- ******* -->
          <!-- Content -->
          <!-- ******* -->
<!-- Top add -->
<CENTER>
<iframe src="http://rcm.amazon.com/e/cm?t=vbhelper&o=1&p=26&l=ur1&category=school&banner=1JCQGP7QZRXQ7N7R56G2&f=ifr" width="468" height="60" scrolling="no" border="0" marginwidth="0" style="border:none;" frameborder="0"></iframe>
</CENTER>

          <TD WIDTH="100%" ALIGN="Left" VALIGN="Top">
            <TABLE WIDTH="100%" BORDER="0" CELLSPACING="0" CELLPADDING="0">
              <!-- How To Summary -->
              <TR><TD>
                <TABLE WIDTH="100%" BORDER="2" CELLSPACING="0" CELLPADDING="2">
                  <TR><TH ALIGN="Left">Title</TH><TD WIDTH="100%">Make a drawing application in VB .NET</TD></TR>
                  <TR><TH ALIGN="Left">Description</TH><TD WIDTH="100%">This example shows how to make a drawing application VB .NET. The user can draw lines, rectangles, ellipses, and stars. It also demonstrates ToolBar dropdowns that display images and complex XML serialization.</TD></TR>
                  <TR><TH ALIGN="Left">Keywords</TH><TD>draw, drawing, Drawable, serialization, XmlSerializer</TD></TR>
                  <TR><TH ALIGN="Left">Categories</TH><TD>Graphics, VB.NET, Software Engineering</TD></TR>
                </TABLE>
              </TD></TR>

              <!-- Text content -->
              <TR><TD>&nbsp;<BR></TD></TR>
              <TR><TD>
This is a very large application so only some key points are described here.
<P>
The Drawable class represents a drawing object (line, rectangle, etc.) that can be specified by a bounding rectangle. It cannot handle things such as curves, free-form drawing, and arbitrary polygons. This class defines the following MustOverride routines. Their names and comments should make them self-explanatory.
              </TD></TR>

              <!-- Code content -->
              <TR><TD>&nbsp;<BR></TD></TR>
              <TR>
                <TD BACKGROUND="computer_paper.jpg">
                  <PRE><FONT NAME="Courier New" POINT-SIZE="10" SIZE="2"><FONT COLOR="#008000">' Draw the object on this Graphics surface.</FONT>
Public MustOverride Sub Draw(ByVal gr As Graphics)

<FONT COLOR="#008000">' Return the object's bounding rectangle.</FONT>
Public MustOverride Function GetBounds() As Rectangle

<FONT COLOR="#008000">' Return True if this point is on the object.</FONT>
Public MustOverride Function IsAt(ByVal x As Integer, ByVal _
    y As Integer) As Boolean

<FONT COLOR="#008000">' The user is moving one of the object's points.</FONT>
Public MustOverride Sub NewPoint(ByVal x As Integer, ByVal _
    y As Integer)

<FONT COLOR="#008000">' Return True if the object is empty (e.g. a zero-length</FONT>
<FONT COLOR="#008000">' line).</FONT>
Public MustOverride Function IsEmpty() As Boolean</FONT></PRE>
              </TD></TR>

              <!-- Text content -->
              <TR><TD>&nbsp;<BR></TD></TR>
              <TR><TD>
The DrawableLine, DrawableRectangle, DrawableEllipse, and DrawableStar classes inherit from Drawable. They implement these routines appropriately for their type of drawing. For example, DrawableRectangle's Draw method draws a rectangle and its IsAt function returns True if a specified point lies within the rectangle.
<P>
The module Geometry.vb contains routines for working with the class's shapes. It has functions for calculating the distance between two points, or between a point and a line segment. It also has a function that can tell if a point lies inside an ellipse.
<P>
<HR WIDTH="75%" COLOR="#00C0FF">
<P>
When the user presses the mouse down, the program creates a new object based on the currently selected tool.
<P>
The MouseMove event handler passes the new object information about the mouse's position by calling its NewPoint method. That method moves the second corner of the object's bounding rectangle.
<P>
When the user releases the mouse, the MouseUp event handler stops drawing the new object and, if the object is not empty, adds it to the picture.
              </TD></TR>

              <!-- Code content -->
              <TR><TD>&nbsp;<BR></TD></TR>
              <TR>
                <TD BACKGROUND="computer_paper.jpg">
                  <PRE><FONT NAME="Courier New" POINT-SIZE="10" SIZE="2"><FONT COLOR="#008000">' Perform an action depending on the currently pushed tool.</FONT>
Private Sub picCanvas_MouseDown(ByVal sender As Object, _
    ByVal e As System.Windows.Forms.MouseEventArgs) Handles _
    picCanvas.MouseDown
    <FONT COLOR="#008000">' See which button was pressed.</FONT>
    If e.Button = MouseButtons.Right Then
        <FONT COLOR="#008000">' Right button. See if we're drawing something.</FONT>
        If m_NewDrawable Is Nothing Then
            <FONT COLOR="#008000">' We are not drawing. Ignore this button.</FONT>
        Else
            <FONT COLOR="#008000">' We are drawing something. Cancel it.</FONT>
            m_Picture.Remove(m_NewDrawable)

            m_NewDrawable = Nothing
            RemoveHandler picCanvas.MouseMove, AddressOf _
                NewDrawable_MouseMove
            RemoveHandler picCanvas.MouseUp, AddressOf _
                NewDrawable_MouseUp

            <FONT COLOR="#008000">' Redraw to erase the new object.</FONT>
            picCanvas.Invalidate()
        End If
    Else
        <FONT COLOR="#008000">' Left button. See which tool is pushed.</FONT>
        Select Case m_SelectedToolButton.ToolTipText
            Case "Pointer"
                <FONT COLOR="#008000">' Select an object.</FONT>
                m_Picture.SelectObjectAt(e.X, e.Y)
            Case "Line"
                <FONT COLOR="#008000">' Start drawing a line.</FONT>
                m_NewDrawable = New _
                    DrawableLine(m_CurrentForeColor, _
                    m_CurrentLineWidth, e.X, e.Y, e.X, e.Y)
                m_Picture.Add(m_NewDrawable)
                AddHandler picCanvas.MouseMove, AddressOf _
                    NewDrawable_MouseMove
                AddHandler picCanvas.MouseUp, AddressOf _
                    NewDrawable_MouseUp
            Case "Rectangle"
                <FONT COLOR="#008000">' Start drawing a rectangle.</FONT>
                m_NewDrawable = New _
                    DrawableRectangle(m_CurrentForeColor, _
                    m_CurrentFillColor, m_CurrentLineWidth, _
                    e.X, e.Y, e.X, e.Y)
                m_Picture.Add(m_NewDrawable)
                AddHandler picCanvas.MouseMove, AddressOf _
                    NewDrawable_MouseMove
                AddHandler picCanvas.MouseUp, AddressOf _
                    NewDrawable_MouseUp
            Case "Ellipse"
                <FONT COLOR="#008000">' Start drawing an ellipse.</FONT>
                m_NewDrawable = New _
                    DrawableEllipse(m_CurrentForeColor, _
                    m_CurrentFillColor, m_CurrentLineWidth, _
                    e.X, e.Y, e.X, e.Y)
                m_Picture.Add(m_NewDrawable)
                AddHandler picCanvas.MouseMove, AddressOf _
                    NewDrawable_MouseMove
                AddHandler picCanvas.MouseUp, AddressOf _
                    NewDrawable_MouseUp
            Case "Star"
                <FONT COLOR="#008000">' Start drawing a star.</FONT>
                m_NewDrawable = New _
                    DrawableStar(m_CurrentForeColor, _
                    m_CurrentFillColor, m_CurrentLineWidth, _
                    e.X, e.Y, e.X, e.Y)
                m_Picture.Add(m_NewDrawable)
                AddHandler picCanvas.MouseMove, AddressOf _
                    NewDrawable_MouseMove
                AddHandler picCanvas.MouseUp, AddressOf _
                    NewDrawable_MouseUp
        End Select

        <FONT COLOR="#008000">' Redraw.</FONT>
        picCanvas.Invalidate()
    End If
End Sub

<FONT COLOR="#008000">' On mouse move, continue drawing.</FONT>
Private Sub NewDrawable_MouseMove(ByVal sender As Object, _
    ByVal e As System.Windows.Forms.MouseEventArgs)
    <FONT COLOR="#008000">' Update the new line's coordinates.</FONT>
    m_NewDrawable.NewPoint(e.X, e.Y)

    <FONT COLOR="#008000">' Redraw to show the new line.</FONT>
    picCanvas.Invalidate()
End Sub

<FONT COLOR="#008000">' On mouse up, finish drawing the new object.</FONT>
Private Sub NewDrawable_MouseUp(ByVal sender As Object, _
    ByVal e As System.Windows.Forms.MouseEventArgs)
    <FONT COLOR="#008000">' No longer watch for MouseMove or MouseUp.</FONT>
    RemoveHandler picCanvas.MouseMove, AddressOf _
        NewDrawable_MouseMove
    RemoveHandler picCanvas.MouseUp, AddressOf _
        NewDrawable_MouseUp

    <FONT COLOR="#008000">' See if the new object is empty (e.g. a zero-length</FONT>
    <FONT COLOR="#008000">' line).</FONT>
    If m_NewDrawable.IsEmpty() Then
        <FONT COLOR="#008000">' Discard this object.</FONT>
        m_Picture.Remove(m_NewDrawable)
    End If

    <FONT COLOR="#008000">' We're no longer working with the new object.</FONT>
    m_NewDrawable = Nothing

    <FONT COLOR="#008000">' Redraw.</FONT>
    picCanvas.Invalidate()
End Sub</FONT></PRE>
              </TD></TR>

              <!-- Text content -->
              <TR><TD>&nbsp;<BR></TD></TR>
              <TR><TD>
This program also shows how to make a ToolBar dropdown that displays images. The following code shows how this works for the line width dropdown. The form's Load event handler starts the process by calling subroutine MakeLineThicknessImages.
<P>
MakeLineThicknessImages saves the current number of images in the imlToolbarButtons ImageList control. If then draws a series of images showing different line thicknesses and adds them to the ImageList.
<P>
At design time, I gave the program a context menu containing menu items named mnuThick1, mnuThick2, and so forth. The context menu is associated with the thickness ComboBox-style ToolBar button.
<P>
The program calls subroutine PrepareThicknessMenu for each of these thickness menus. This routine adds Click, MeasureItem, and DrawItem event handlers for the menu item. It also sets the item's OwnerDraw property to True so it generates the MeasureItem and DrawItem events.
              </TD></TR>

              <!-- Code content -->
              <TR><TD>&nbsp;<BR></TD></TR>
              <TR>
                <TD BACKGROUND="computer_paper.jpg">
                  <PRE><FONT NAME="Courier New" POINT-SIZE="10" SIZE="2"><FONT COLOR="#008000">' Get ready.</FONT>
Private Sub Form1_Load(ByVal sender As Object, ByVal e As _
    System.EventArgs) Handles MyBase.Load
    ...
    <FONT COLOR="#008000">' Make line color images.</FONT>
    MakeLineThicknessImages()

    <FONT COLOR="#008000">' Set line thickness menu event handlers.</FONT>
    PrepareThicknessMenu(mnuThick1)
    PrepareThicknessMenu(mnuThick2)
    PrepareThicknessMenu(mnuThick3)
    PrepareThicknessMenu(mnuThick4)
    PrepareThicknessMenu(mnuThick5)
    mnuThick1.PerformClick()
    ...
End Sub

<FONT COLOR="#008000">' Make line thickness images.</FONT>
Private Sub MakeLineThicknessImages()
    Dim bm As New Bitmap(16, 16)
    Dim gr As Graphics = Graphics.FromImage(bm)

    m_FirstLineThicknessImage = _
        imlToolbarButtons.Images.Count
    For i As Integer = 1 To 5
        gr.Clear(SystemColors.Control)
        gr.DrawLine(New Pen(Color.Black, i), 0, 8, 16, 8)
        imlToolbarButtons.Images.Add(bm)
    Next i
End Sub

<FONT COLOR="#008000">' Add Click, MeasureItem, and DrawItem event handlers</FONT>
<FONT COLOR="#008000">' to this MenuItem.</FONT>
Private Sub PrepareThicknessMenu(ByVal menu_item As _
    MenuItem)
    AddHandler menu_item.Click, AddressOf mnuLineThick_Click
    AddHandler menu_item.MeasureItem, AddressOf _
        mnuLineThick_MeasureItem
    AddHandler menu_item.DrawItem, AddressOf _
        mnuLineThick_DrawItem
    menu_item.OwnerDraw = True
End Sub</FONT></PRE>
              </TD></TR>

              <!-- Text content -->
              <TR><TD>&nbsp;<BR></TD></TR>
              <TR><TD>
The mnuLineThick_Click event handler finds the menu item object that raised the Click event and checks its Text property to get the item's selected thickness. It sets the ComboBox ToolBar button's image to the one with the right thickness. It then sets the thickness for the currently selected object, if there is one.
<P>
The mnuLineThick_MeasureItem event handler tells VB thaht the menu item needs a 16x16 pixel area. VB will allocate a space at least this large. In practice, the area will be wider than this.
<P>
The mnuLineThick_DrawItem event handler determines whetrher the menu item is currently selected (the mouse is over it) and picks appropriate colors. It erases the menu item's background and then draws a line with the correct thickness.
              </TD></TR>

              <!-- Code content -->
              <TR><TD>&nbsp;<BR></TD></TR>
              <TR>
                <TD BACKGROUND="computer_paper.jpg">
                  <PRE><FONT NAME="Courier New" POINT-SIZE="10" SIZE="2"><FONT COLOR="#008000">' The user has selected a new line thickness.</FONT>
Private Sub mnuLineThick_Click(ByVal sender As _
    System.Object, ByVal e As System.EventArgs)
    <FONT COLOR="#008000">' Update the current pen.</FONT>
    Dim menu_item As MenuItem = DirectCast(sender, MenuItem)
    m_CurrentLineWidth = Integer.Parse(menu_item.Text)

    <FONT COLOR="#008000">' Update the toolbar display.</FONT>
    tcboThickness.ImageIndex = m_CurrentLineWidth + _
        m_FirstLineThicknessImage - 1

    <FONT COLOR="#008000">' Update the selected object if there is one.</FONT>
    If Not (m_Picture.SelectedDrawable Is Nothing) Then
        m_Picture.SelectedDrawable.LineWidth = _
            m_CurrentLineWidth
        picCanvas.Invalidate()
    End If

    <FONT COLOR="#008000">' Reselect the currently selected tool.</FONT>
    m_SelectedToolButton.Pushed = True
End Sub

<FONT COLOR="#008000">' Allow room for the MenuItem.</FONT>
Private Sub mnuLineThick_MeasureItem(ByVal sender As _
    Object, ByVal e As _
    System.Windows.Forms.MeasureItemEventArgs)
    e.ItemWidth = 16
    e.ItemHeight = 16
End Sub

<FONT COLOR="#008000">' Draw the menu item.</FONT>
Private Sub mnuLineThick_DrawItem(ByVal sender As Object, _
    ByVal e As System.Windows.Forms.DrawItemEventArgs)
    Dim menu_item As MenuItem = DirectCast(sender, MenuItem)
    Dim thickness As Integer = Integer.Parse(menu_item.Text)

    <FONT COLOR="#008000">' See if we're selected.</FONT>
    Dim fg_pen As Pen
    Dim bg_brush As Brush
    If (e.State And DrawItemState.Selected) = 0 Then
        <FONT COLOR="#008000">' Not selected.</FONT>
        <FONT COLOR="#008000">' Use a light background and dark foreground.</FONT>
        fg_pen = New Pen(SystemColors.MenuText, thickness)
        bg_brush = New SolidBrush(SystemColors.Menu)
    Else
        <FONT COLOR="#008000">' Selected.</FONT>
        <FONT COLOR="#008000">' Use a dark background and light foreground.</FONT>
        fg_pen = New Pen(SystemColors.HighlightText, _
            thickness)
        bg_brush = New SolidBrush(SystemColors.Highlight)
    End If

    <FONT COLOR="#008000">' Erase the background.</FONT>
    e.Graphics.FillRectangle(bg_brush, e.Bounds)

    <FONT COLOR="#008000">' Draw the line.</FONT>
    Dim y As Integer = e.Bounds.Y + e.Bounds.Height \ 2
    e.Graphics.DrawLine(fg_pen, e.Bounds.X, y, _
        e.Bounds.Right, y)

    fg_pen.Dispose()
    bg_brush.Dispose()
End Sub</FONT></PRE>
              </TD></TR>

              <!-- Text content -->
              <TR><TD>&nbsp;<BR></TD></TR>
              <TR><TD>
This program also shows how to save and restore pictures. Each of the drawing classes, including the DrawablePicture class that holds the objects in a picture, has a Serializable attribute. That tells VB to include information that allows an XmlSerializer object to serialize the class.
<P>
The classes include other attributes to tell the serializer how to treat certain properties. For instance, the following code shows part of the Drawable class. Attributes indicate that the ForeColor, FillColor, and IsSelected properties should not be serialized. LineWidth, X1, Y1, X2, and Y2 should be serialized as attributes of the Drawable object, not as separate elements inside it in the XML result.
              </TD></TR>

              <!-- Code content -->
              <TR><TD>&nbsp;<BR></TD></TR>
              <TR>
                <TD BACKGROUND="computer_paper.jpg">
                  <PRE><FONT NAME="Courier New" POINT-SIZE="10" SIZE="2">Imports System.Xml.Serialization

&lt;Serializable()&gt; _
Public MustInherit Class Drawable
    <FONT COLOR="#008000">' Drawing characteristics.</FONT>
    &lt;XmlIgnore()&gt; Public ForeColor As Color
    &lt;XmlIgnore()&gt; Public FillColor As Color
    &lt;XmlAttributeAttribute()&gt; Public LineWidth As Integer = _
        0

    &lt;XmlAttributeAttribute()&gt; Public X1 As Integer
    &lt;XmlAttributeAttribute()&gt; Public Y1 As Integer
    &lt;XmlAttributeAttribute()&gt; Public X2 As Integer
    &lt;XmlAttributeAttribute()&gt; Public Y2 As Integer

    <FONT COLOR="#008000">' Indicates whether we should draw as selected.</FONT>
    &lt;XmlIgnore()&gt; Public IsSelected As Boolean = False
    ...
End Class</FONT></PRE>
              </TD></TR>

              <!-- Text content -->
              <TR><TD>&nbsp;<BR></TD></TR>
              <TR><TD>
There are a couple of tricks here. Some properties are not serialized because they are not needed when you reload a picture. There's no need to remember whether an object was selected when you saved the file.
<P>
Some VB and system objects such as Color do not include information for serialization. If you try to serialize one, you get an empty element. To work around this, the Drawable class provides public properties that represent its colors as ARGB values. Those are integers so they are easy to serialize. The following code shows the property procedures for the ForeColor.
              </TD></TR>

              <!-- Code content -->
              <TR><TD>&nbsp;<BR></TD></TR>
              <TR>
                <TD BACKGROUND="computer_paper.jpg">
                  <PRE><FONT NAME="Courier New" POINT-SIZE="10" SIZE="2">&lt;XmlAttributeAttribute("ForeColor")&gt; _
Public Property ForeColorArgb() As Integer
    Get
        Return ForeColor.ToArgb()
    End Get
    Set(ByVal Value As Integer)
        ForeColor = Color.FromArgb(Value)
    End Set
End Property</FONT></PRE>
              </TD></TR>

              <!-- Text content -->
              <TR><TD>&nbsp;<BR></TD></TR>
              <TR><TD>
The DrawablePicture class stores the picture's objects in an ArrayList object. To serialize an ArrayList, you need to use the XmlElement attribute to tell VB what types of objects the ArrayList might contain.
              </TD></TR>

              <!-- Code content -->
              <TR><TD>&nbsp;<BR></TD></TR>
              <TR>
                <TD BACKGROUND="computer_paper.jpg">
                  <PRE><FONT NAME="Courier New" POINT-SIZE="10" SIZE="2"><FONT COLOR="#008000">' The list where we will store objects.</FONT>
&lt;XmlElement(GetType(Drawable)), _
 XmlElement(GetType(DrawableLine)), _
 XmlElement(GetType(DrawableRectangle)), _
 XmlElement(GetType(DrawableEllipse)), _
 XmlElement(GetType(DrawableStar))&gt; _
Public Drawables As New ArrayList</FONT></PRE>
              </TD></TR>

              <!-- Text content -->
              <TR><TD>&nbsp;<BR></TD></TR>
              <TR><TD>
The DrawablePicture class's SavePicture and LoadPicture methods save and load picture serializations. SavePicture makes an XmlSerializer initialized to work with DrawablePicture objects. It makes a StreamWriter attached to the output file and uses the XmlSerializer to serialize the picture into it.
<P>
LoadPicture makes an XmlSerializer initialized to work with DrawablePicture objects. It makes a StreamWriter attached to the input file and uses the XmlSerializer to read a DrawablePicture object out of it. It then returns this new object.
              </TD></TR>

              <!-- Code content -->
              <TR><TD>&nbsp;<BR></TD></TR>
              <TR>
                <TD BACKGROUND="computer_paper.jpg">
                  <PRE><FONT NAME="Courier New" POINT-SIZE="10" SIZE="2"><FONT COLOR="#008000">' Save the picture into the file.</FONT>
Public Sub SavePicture(ByVal file_name As String)
    Try
        Dim xml_serializer As New _
            XmlSerializer(GetType(DrawablePicture))
        Dim stream_writer As New StreamWriter(file_name)
        xml_serializer.Serialize(stream_writer, Me)
        stream_writer.Close()
    Catch ex As Exception
        If MessageBox.Show(ex.Message &amp; vbCrLf &amp; _
            "Show internal error?", "Save Error", _
            MessageBoxButtons.YesNo, _
                MessageBoxIcon.Question) = DialogResult.Yes _
                Then
            MessageBox.Show(ex.InnerException.ToString, _
                "Internal Error", _
                 MessageBoxButtons.OK, _
                     MessageBoxIcon.Exclamation)
        End If
    End Try
End Sub

<FONT COLOR="#008000">' Laod the picture from the file.</FONT>
Public Shared Function LoadPicture(ByVal file_name As _
    String) As DrawablePicture
    Try
        Dim xml_serializer As New _
            XmlSerializer(GetType(DrawablePicture))
        Dim file_stream As New FileStream(file_name, _
            FileMode.Open)
        Dim new_picture As DrawablePicture = _
            DirectCast(xml_serializer.Deserialize(file_stream), _
                DrawablePicture)
        file_stream.Close()
        Return new_picture
    Catch ex As Exception
        If MessageBox.Show(ex.Message &amp; vbCrLf &amp; _
            "Show internal error?", "Save Error", _
            MessageBoxButtons.YesNo, _
                MessageBoxIcon.Question) = DialogResult.Yes _
                Then
            MessageBox.Show(ex.InnerException.ToString, _
                "Internal Error", _
                 MessageBoxButtons.OK, _
                     MessageBoxIcon.Exclamation)
        End If
        Return Nothing
    End Try
End Function</FONT></PRE>
              </TD></TR>

              <!-- Text content -->
              <TR><TD>&nbsp;<BR></TD></TR>
              <TR><TD>
To let the user move objects, the program looks for MouseMove events. If the left button is down, the main form calls the picture's MoveSelectedDrawableToMouse method to move the currently selected Drawable. It then invalidates the picture to redraw it.
              </TD></TR>

              <!-- Code content -->
              <TR><TD>&nbsp;<BR></TD></TR>
              <TR>
                <TD BACKGROUND="computer_paper.jpg">
                  <PRE><FONT NAME="Courier New" POINT-SIZE="10" SIZE="2"><FONT COLOR="#008000">' If we have an object selected, move it.</FONT>
Private Sub picCanvas_MouseMove(ByVal sender As Object, _
    ByVal e As System.Windows.Forms.MouseEventArgs) Handles _
    picCanvas.MouseMove
    <FONT COLOR="#008000">' Only move if the left button is down.</FONT>
    If e.Button = Windows.Forms.MouseButtons.Left Then
        <FONT COLOR="#008000">' Move it.</FONT>
        m_Picture.MoveSelectedDrawableToMouse(e.X, e.Y)

        <FONT COLOR="#008000">' Redraw to show the new position.</FONT>
        picCanvas.Invalidate()
    End If
End Sub</FONT></PRE>
              </TD></TR>

              <!-- Text content -->
              <TR><TD>&nbsp;<BR></TD></TR>
              <TR><TD>
The DrawablePicture's MoveSelectedDrawableToMouse method exits if no Drawable is selected.
<P>
If an object is selected, the program calculates the distance the mouse has been moved and calls the selected Drawable's MoveRelative method to move it by a corresponding amount. It then saves the new mouse position for future moves.
<P>
(Note: The mouse's position is saved in (m_SelectedMouseX, m_SelectedMouseY) when a Drawable is selected so it is ready to move.)
              </TD></TR>

              <!-- Code content -->
              <TR><TD>&nbsp;<BR></TD></TR>
              <TR>
                <TD BACKGROUND="computer_paper.jpg">
                  <PRE><FONT NAME="Courier New" POINT-SIZE="10" SIZE="2"><FONT COLOR="#008000">' Move the selected drawable. The mouse has moved from</FONT>
<FONT COLOR="#008000">' (m_SelectedMouseX, m_SelectedMouseY) to (x, y).</FONT>
Public Sub MoveSelectedDrawableToMouse(ByVal x As Integer, _
    ByVal y As Integer)
    <FONT COLOR="#008000">' Do nothing if nothing is selected.</FONT>
    If SelectedDrawable Is Nothing Then Exit Sub

    <FONT COLOR="#008000">' See how far we want it moved.</FONT>
    Dim new_dx As Integer = x - m_SelectedMouseX
    Dim new_dy As Integer = y - m_SelectedMouseY

    <FONT COLOR="#008000">' Move it.</FONT>
    SelectedDrawable.MoveRelative(new_dx, new_dy)

    <FONT COLOR="#008000">' Save the new mouse position.</FONT>
    m_SelectedMouseX = x
    m_SelectedMouseY = y
End Sub</FONT></PRE>
              </TD></TR>

              <!-- Text content -->
              <TR><TD>&nbsp;<BR></TD></TR>
              <TR><TD>
The program uses a PrintDocument and PrintPreviewDialog to print the drawing and to display a print preview. The most interesting part of this code is the PrintPage event handler that the is called to generate the printout.
<P>
The actual printing is done by a simple call to:
<P>
<PRE>    m_Picture.Draw(e.Graphics)</PRE>
<P>
The rest of the code scales the picture to fill the printable area and translates it to center the result.
              </TD></TR>

              <!-- Code content -->
              <TR><TD>&nbsp;<BR></TD></TR>
              <TR>
                <TD BACKGROUND="computer_paper.jpg">
                  <PRE><FONT NAME="Courier New" POINT-SIZE="10" SIZE="2">    Private Sub pdPrint_PrintPage(ByVal sender As _
        System.Object, ByVal e As _
        System.Drawing.Printing.PrintPageEventArgs) Handles _
        pdPrint.PrintPage
#Const PRINT_CENTERED = True
#Const PRINT_ENLARGED = True
        <FONT COLOR="#008000">'#Const PRINT_MARGIN = True</FONT>

#If PRINT_CENTERED Then <FONT COLOR="#008000">' Center the picture.</FONT>
        <FONT COLOR="#008000">' Get the picture's bounds.</FONT>
        Dim bounds As Rectangle = m_Picture.GetBounds()

        <FONT COLOR="#008000">' Translate the drawing to the origin.</FONT>
        e.Graphics.TranslateTransform(-bounds.X, -bounds.Y)

        Dim scale As Single = 1

#If PRINT_ENLARGED Then <FONT COLOR="#008000">' Scale to fit.</FONT>
        Dim xscale As Double = 1
        If bounds.Width &gt; 0 Then xscale = _
            e.MarginBounds.Width / bounds.Width
        Dim yscale As Double = 1
        If bounds.Height &gt; 0 Then yscale = _
            e.MarginBounds.Height / bounds.Height

        If xscale &gt; yscale Then
            scale = CSng(yscale)
        Else
            scale = CSng(xscale)
        End If
        e.Graphics.ScaleTransform(scale, scale, _
            Drawing2D.MatrixOrder.Append)
#End If

        <FONT COLOR="#008000">' Translate to center the drawing.</FONT>
        Dim cx As Integer = CInt((e.MarginBounds.Width - _
            bounds.Width * scale) / 2)
        Dim cy As Integer = CInt((e.MarginBounds.Height - _
            bounds.Height * scale) / 2)
        e.Graphics.TranslateTransform( _
            e.MarginBounds.X + cx, _
            e.MarginBounds.Y + cy, _
            Drawing2D.MatrixOrder.Append)
#End If

        <FONT COLOR="#008000">' Draw the picture.</FONT>
        m_Picture.Draw(e.Graphics)

#If PRINT_MARGIN Then
        <FONT COLOR="#008000">' Draw the margin.</FONT>
        e.Graphics.ResetTransform()
        Using margin_pen As New Pen(Color.Red)
            margin_pen.DashPattern = New Single() {5, 5}
            e.Graphics.DrawRectangle(margin_pen, _
                e.MarginBounds)
        End Using
#End If
    End Sub</FONT></PRE>
              </TD></TR>

              <!-- Text content -->
              <TR><TD>&nbsp;<BR></TD></TR>
              <TR><TD>
This example also shows how to change the stacking order of the objects it draws, and how to delete objects. See the code for additional details.
              </TD></TR>

              <!-- *************** -->
              <!-- Download button -->
              <!-- *************** -->

              <TR><TD><FONT SIZE="-2">&nbsp;</FONT></TD></TR>
              <TR><TD ALIGN="Center"><A HREF="HowTo/howto_net_drawing_framework.zip"><IMG SRC="download.jpg" BORDER="0"></A></TD></TR>
              <TR><TD><FONT SIZE="-2">&nbsp;</FONT></TD></TR>

            </TABLE> <!-- End content table -->
          </TD> <!-- End content column -->

          <!-- ************** -->
          <!-- Google AdSense -->
          <!-- ************** -->
          <TD WIDTH="120" ALIGN="Right" VALIGN="Top">
            <TABLE WIDTH="165" BORDER="0" CELLSPACING="0" CELLPADDING="0">
              <TR><TD ALIGN="Right">

<script type="text/javascript"><!--
google_ad_client = "pub-6627515316741006";
google_ad_width = 160;
google_ad_height = 600;
google_ad_format = "160x600_as";
google_ad_channel ="4974732445";
google_color_border = "00CC66";
google_color_bg = "99FF99";
google_color_link = "0000FF";
google_color_url = "0033FF";
google_color_text = "000000";
//--></script>
<script type="text/javascript"
  src="http://pagead2.googlesyndication.com/pagead/show_ads.js">
</script>

              </TD></TR>
            </TABLE> <!-- End Google AdSense table -->
          </TD> <!-- End Google AdSense column -->


        </TR> <!-- End row containing menu column and content -->
      </TABLE> <!-- End table containing menu column and content -->
    </TD></TR>

    <!-- ****** -->
    <!-- Footer -->
    <!-- ****** -->
    <TR><TD WIDTH="100%">
      <TABLE WIDTH="100%" BORDER="0" BGCOLOR="#87CEFA" CELLSPACING="0" CELLPADDING="0">
        <!-- Top row -->
        <TR>
          <TD BGCOLOR="#E1F3FF" ALIGN="Right" WIDTH="16"><IMG SRC="blue_ul.jpg" WIDTH="16" HEIGHT="16"></TD>
          <TD BACKGROUND="blue_um.jpg" COLSPAN="2" ALIGN="Center"><FONT SIZE="-2">
            Copyright &copy; 1997-2008 Rocky Mountain Computer Consulting, Inc. &nbsp; All rights reserved.
          </FONT></TD>
          <TD BGCOLOR="#E1F3FF"><IMG SRC="blue_ur.jpg" WIDTH="16" HEIGHT="16"></TD>
        </TR>

        <!-- Bottom row -->
        <TR>
          <TD BGCOLOR="#E1F3FF" ALIGN="Right" WIDTH="16"><IMG SRC="blue_ll.jpg" WIDTH="16" HEIGHT="16"></TD>
          <TD BACKGROUND="blue_lm.jpg"><FONT SIZE="-2">
            &nbsp;<SCRIPT LANGUAGE=JavaScript>document.write(document.URL)</SCRIPT></FONT></TD>
          <TD BACKGROUND="blue_lm.jpg" ALIGN="Right"><FONT SIZE="-2">
            Updated <SCRIPT LANGUAGE=JavaScript>document.write(document.lastModified)</SCRIPT> &nbsp;
          </FONT></TD>
          <TD BGCOLOR="#E1F3FF"><IMG SRC="blue_lr.jpg" WIDTH="16" HEIGHT="16"></TD>
        </TR>
      </TABLE>
    </TD></TR>
  </TABLE> <!-- End table containing banner, menu column + content, and footer -->

</BODY>
</HTML>